package commons.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;

import org.apache.commons.configuration.ConfigurationException;

import planning.heuristic.OptimalHeuristic;
import planning.heuristic.PlanningFitnessFunction;

import commons.cloud.Request;
import commons.config.Configuration;
import commons.sim.util.SaaSUsersProperties;

/**
 * This class reads a config.properties that is used in simulation and extracts user traces to be used. For each user trace
 * it calculates some statistics per fixed intervals (e.g. per hour) and creates new "user traces" to be used by {@link OptimalHeuristic} during capacity planning.
 * 
 * @author David Candeia Medeiros Maia - davidcmm@lsd.ufcg.edu.br
 */
public class GenerateWorkloadGraphData {
	
	private static final String DEFAULT_OUTPUT_FILE = "demand_";
	
	private static int currentTick;
	private static int tick;
	private static ArrayList<Request> leftOver;
	
	public static long ids = 0;
	
	public static void main(String[] args) throws ConfigurationException, IOException {
		if(args.length != 1){
			throw new RuntimeException("usage: <config file>");
		}
		
		Configuration.buildInstance(args[0]);
		Configuration config = Configuration.getInstance();
		
		String[] workloads = config.getWorkloads();
		int[] clientsID = config.getIntegerArray(SaaSUsersProperties.SAAS_USER_ID);
		
		tick = PlanningFitnessFunction.HOUR_IN_MILLIS;
		Map<Integer, Map<Integer, Double>> clientsHourDemand = new HashMap<Integer, Map<Integer,Double>>(); 
		
		for(int i = 0; i < workloads.length; i++){//Each SaaS client
			BufferedReader pointersReader = new BufferedReader(new FileReader(workloads[i]));
			
			clientsHourDemand.put(clientsID[i], new HashMap<Integer, Double>());
			
			int hourIndex = 1;
			while(pointersReader.ready()){//Each SaaS client workload day
				String pointersFile = pointersReader.readLine();
				BufferedReader workloadReader = new BufferedReader(new FileReader(pointersFile));
				leftOver = new ArrayList<Request>();
				currentTick = 0;
				
				while(workloadReader.ready()){
					List<Request> requests = next(workloadReader, clientsID[i]);
					double totalDemandInHours = sumDemand(requests);
					
					Map<Integer, Double> demand = clientsHourDemand.get(clientsID[i]);
					demand.put(hourIndex, totalDemandInHours);
					hourIndex++;
				}
				
				workloadReader.close();
			}
			pointersReader.close();
		}
		
		persistDemand(clientsHourDemand);
	}
	
	private static void persistDemand(Map<Integer, Map<Integer, Double>> clientsHourDemand) throws IOException {
		for(Entry<Integer, Map<Integer, Double>> entry : clientsHourDemand.entrySet()){
			int clientID = entry.getKey();
			Map<Integer, Double> demand = entry.getValue();
			BufferedWriter usersPropertiesWriter = new BufferedWriter(new FileWriter(DEFAULT_OUTPUT_FILE+clientID+".out"));
			
			for(Entry<Integer, Double> entry2 : demand.entrySet()){
				usersPropertiesWriter.write(entry2.getKey()+"\t"+entry2.getValue()+"\n");
			}
			usersPropertiesWriter.flush();
			usersPropertiesWriter.close();
		}
	}

	private static double sumDemand(List<Request> requests) {
		double totalServiceTimeInMillis = 0d;
		for(Request request : requests){
			totalServiceTimeInMillis += request.getMeanToProcess();
		}
		return totalServiceTimeInMillis / (1000 * 60 * 60);
	}
	
	/**
	 * Reading a list of requests from workload files
	 * @param workloadReader
	 * @param clientID
	 * @return
	 * @throws IOException
	 */
	public static List<Request> next(BufferedReader workloadReader, int clientID) throws IOException{
		List<Request> requests = new ArrayList<Request>(leftOver);
		
		long time = (currentTick + 1) * tick;
		leftOver.clear();
		for (Request request : requests) {
			if(request.getArrivalTimeInMillis() >= time){
				leftOver.add(request);
			}
		}
		requests.removeAll(leftOver);
		
		while(workloadReader.ready()){
			Request request = parseRequest(workloadReader.readLine(), clientID);
			if(request.getArrivalTimeInMillis() < time){
				requests.add(request);
			}else{
				leftOver.add(request);
				break;
			}
		}
		
		currentTick++;
		return requests;
	}
	
	/**
	 * Parsing a request from a string in the workload file
	 * @param line String corresponding to a line of workload file
	 * @param saasClientID SaaS client ID
	 * @return
	 */
	protected static Request parseRequest(String line, int saasClientID) {
		StringTokenizer tokenizer = new StringTokenizer(line, "( +|\t+)+");

		int userID = Integer.parseInt(tokenizer.nextToken());
		tokenizer.nextToken();

		long reqID = ids++;
		long arrivalTimeInMillis = Long.parseLong(tokenizer.nextToken());
		long requestSizeInBytes = Long.parseLong(tokenizer.nextToken());
		long responseSizeInBytes = Long.parseLong(tokenizer.nextToken());
		
		long [] demand = new long[tokenizer.countTokens()];
		int index = 0;
		while(tokenizer.hasMoreTokens()){
			demand[index++] = (long)Double.parseDouble(tokenizer.nextToken());
		}
		
		return new Request(reqID, saasClientID, userID, arrivalTimeInMillis,
				requestSizeInBytes, responseSizeInBytes, demand);
	}
	

}
